II.3.PROGRAMMES ET PROGRAMMATION:
II.3.1.NOTION D'ALGORITHME:
II.3.1.1.DEFINITION:
Un ALGORITHME représente l'énoncé d'une procédure de résolution opérationnelle d'un problème donné,
écrit dans un langage compréhensible pour l'EXECUTANT (ou EXECUTEUR) qui est chargé de la mettre
en oeuvre.
Un algorithme se présente sous la forme d'une suite finie de directives. Un algorithme est valide s'il est
capable de résoudre le problème pour lequel il a été créé en exécutant un nombre fini de directives.
EXEMPLES:
- On nomme «algorithme de la division» la procédure décrivant pas à pas la méthode de réalisation
d'une division de deux nombres (ne pas confondre avec la «théorie de la division», qui justifie cette méthode).
- Une recette de cuisine peut être assimilée à un algorithme décrivant une procédure de préparation d'un plat.
II.3.1.2.CARACTERISTIQUES PRINCIPALES:
LES DIRECTIVES:
Il est possible de classer les directives d'un langage algorithmique en trois catégories: les
directives séquencielles, les alternatives et les répétitives.
DIRECTIVES SEQUENCIELLES:
La plupart des directives spécifient une ou des opérations à effectuer impérativement et doivent être
exécutées dans l'ordre ou elles sont énoncées:
EXEMPLE:
- Placer 300 g de farine dans un saladier
- Mélanger avec 3 cuillerées d'huile d'olives
- Ajouter 100 grammes d'eau
- etc...
DIRECTIVES ALTERNATIVES:
Cependant, il peut également exister des directives qui subordonnent la suite des directives à exécuter
à la réalisation d'une condition:
EXEMPLE:
SI ma destination se trouve dans la ville ALORS je prends mon scooter SINON je prends mon automobile.
DIRECTIVES REPETITIVES:
Les directives répétitives permettent de répéter une séquence de directives jusqu'à ce qu'un condition soit remplie.
LES DONNEES:
Les directives d'un algorithme agissent sur un certain nombre de DONNEES: dans les exemples ci-dessus,
«farine» ou «destination» sont des données. L'ensemble des données qu'un algorithme utilise, crée ou
modifie constitue l'ESPACE D'EXECUTION» de cet algorithme.
Dans un algorithme, chaque donnée doit être DECLAREE. Cette opération consiste à lui attribuer un TYPE
(Type numérique, logique, textuel, tableau de données, etc.). La déclaration d'une donnée s'écrit souvent
comme suit:
EXEMPLE (déclaration de la donnée PRIX_DE_REVIENT):
var PRIX_DE_REVIENT en NUMERIQUE
REMARQUE:
Les langages algorithmiques sont, par principe, très proches des langages «humains». Cependant,
il existe certaines conventions d'écriture, qui se traduisent par des mots clefs (les SI, ALORS, SINON,
TANT QUE, FINFAIRE, etc.. utilisés dans les exemples), le typage des données et des manières de
disposer les directives (indentation).
II.3.2.NOTION DE PROGRAMME:
II.3.2.1.DEFINITION:
En informatique, un PROGRAMME n'est autre que la traduction d'un algorithme dans un
langage opérationnel pouvant être exécuté par un ordinateur (LANGAGE DE PROGRAMMATION).
De ce fait, un programme se présente sous la forme d'une suite finie d'instructions, agissant sur un ensemble
fini de données. Un programme est valide s'il est capable de résoudre le problème pour lequel il a été créé
en exécutant un nombre fini d'instructions.
II.3.2.2.REPRESENTATION D'UN PROGRAMME EN MEMOIRE
Pour qu'un programme puisse être exécuté directement par un PROCESSEUR tel que nous l'avons décrit
plus haut, il doit être écrit dans le LANGAGE INTERNE de ce processeur. Sinon, il faudra passer par une phase
de traduction automatique du programme en langage interne, que l'on appelle COMPILATION ou INTERPRETATION,
suivant la manière dont elle est réalisée.
Il faudra d'autre part charger ce programme en mémoire. Lors de son exécution, un programme occupera donc un
ensemble de cases de la mémoire centrale, certaines étant allouées à ses instructions et d'autres à ses données.
II.3.2.3.STRUCTURE D'UNE INSTRUCTION
Chaque type de processeur informatique possède un langage interne qui lui est propre, bien que les
processeurs appartenant à une même gamme (pentium, celeron, athlon...) aient des jeux d'instruction
très semblables. Cependant, on peut distinguer quelques caractéristiques communes à tous les langages
internes.
Ainsi, l'enregistrement représentant une instruction en langage interne est toujours composé de trois
champs de données distincts:
CODE D'ORDRE | MODE D'ADRESSAGE | OPERANDE(S) |
CODE D'ORDRE:
C'est un code numérique (un nombre entier positif) permettant d'identifier le type d'opération (addition,
soustraction, lecture ou écriture en mémoire, etc.).
MODE D'ADRESSAGE:
Il s'agit également d'un identificateur permettant de déterminer le «mode d'adressage des opérandes».
Nous verrons plus loin ce que recouvre ce concept.
OPERANDES:
Ce champ, combiné au champ précédent, permet d'accéder à la valeur courante de chacun des opérandes
utilisés par l'operation. Selon le type d'instruction, il peut représenter un seul opérande (cas le
plus fréquent) ou être divisé en plusieurs sous-champs, chacun représentant un opérande.
Un champ d'opérande contient toujours un nombre entier. Ce nombre peut représenter directement la valeur
de l'opérande (instruction à opérande immédiat). Il peut également représenter une adresse mémoire (il
est alors positif ou nul). Dans ce dernier cas, suivant le mode d'adressage, ce nombre peut représenter:
- L'adresse mémoire de l'opérande (adressage direct).
- L'adresse de l'adresse mémoire de l'opérande (adressage indirect).
- L'adresse de début d'un tableau de données dans lequel se trouve l'opérande. Pour trouver l'adresse de
l'opérande, l'unité de traitement ajoutera à l'adresse de début le contenu d'un registre contenant la
valeur d'un nombre entier positif appelé index (adressage indexé – le registre est appelé REGISTRE D'INDEX).
- Ces modes peuvent se combiner entre eux.
EXEMPLE:
Soit une instruction permettant de lire une donnée en mémoire et de la placer dans l'accumulateur.
Représentons par LMA ( Load Memory to Accumulator) son code d'ordre. Supposons que cette instruction n'ait
qu'un seul opérande, dont la valeur est le nombre 1000.
Supposons également:
- Que le registre d'index contient la valeur 5.
- Que la case mémoire d'adresse 1000 contient la valeur 1001.
- Que la case mémoire d'adresse 1001 contient la valeur -2.
- Que la case mémoire d'adresse 1005 contient la valeur 24.
Suivant le contenu du champ MODE D'ADRESSAGE, le contenu de l'accumulateur en fin d'exécution sera:
TYPE D'OPERANDE |
CONTENU DE L'ACCUMULATEUR |
COMMENTAIRE |
Opérande Immédiat |
1000 |
C'est la valeur immédiate de l'opérande |
Adressage direct |
1001 |
La valeur 1000 de l'opérande est interprétée comme l'adresse de l'opérande. La case 1000
contenant 1001, c'est cette valeur qui sera chargée dans l'accumulateur. |
Adressage indirect |
-2 |
La valeur 1000 de l'opérande est interprétée comme l'adresse d'une case dans laquelle
on trouvera l'adresse réelle de l'opérande. La case 1000 contenant 1001, on ira chercher
la valeur contenue dans la case 1001, c'est à dire -2. |
Adressage indexé |
24 |
La valeur 1000 de l'opérande est interprétée comme étant l'adresse d'un tableau de valeurs,
dans lequel le rang de l'opérande est indiqué par le contenu du registre d'index. Ce contenu
étant 5, c'est le contenu de la case 1005 (1000+5) qui sera chargé dans l'accumulateur,
c'est à dire 24. |
MEME CHOSE SOUS FORME DE SCHEMA:
II.3.3.INSTRUCTIONS DES LANGAGES INTERNES:
II.3.3.1.INSTRUCTIONS SEQUENCIELLES:
Ce sont des instructions qui ne modifient pas l'ordre d'exécution séquenciel du programme
(si l'instruction est placée dans la case N, la prochaine instruction sera celle de la case N+1).
Elles correspondent donc aux directives séquencielles des algorithmes). On trouve essentiellement:
- Des instructions permettant de lire la mémoire et de charger le contenu dans un registre
(accumulateur, registre d'index, registre d'entrée-sortie, etc.).
- Des instructions permettant de lire un registre et de charger son contenu en mémoire.
- Des instruction lisant un registre et chargeant son contenu dans un autre registre.
- Des instructions déclenchant les entrées-sorties de données (envoi du contenu du registre
d'entrée-sortie vers la périphérie ou l'inverse).
- Des instructions de calcul numérique (exemple: addition de l'accumulateur avec la mémoire,
résultat dans l'accumulateur).
- Etc.
II.3.3.2.INSTRUCTIONS INTERROMPANT LA SEQUENCE D'EXECUTION:
Ces instructions sont capables de modifier la séquence d'instructions en aiguillant l'exécution vers une
adresse quelconque, différente de l'adresse suivante. L'opération revient à modifier le contenu du
COMPTEUR ORDINAL en chargeant dans celui-ci la valeur d'un opérande. Cette rupture de séquence peut être:
INCONDITIONNELLE:
L'exécution de l'instruction de rupture modifie toujours la valeur du compteur ordinal. Ce type
d'instruction n'existe pas dans un langage algorithmique, car les directives d'un algorithme
n'ont pas d'adresse. Elle correspondrait à une directive du type:
CONTINUER L'EXECUTION à l'adresse N
CONDITIONNELLE:
La modification du compteur ordinal est subordonnée à la réalisation d'une condition au moment de son
exécution (une condition sur la valeur d'un registre, par exemple). Si la condition est réalisée,
l'exécutant est dérouté vers l'adresse spécifiée par l'instruction. Sinon, l'exécutant continue en
séquence. Ce type d'instruction correspond à une directive de langage algorithmique libellée comme suit:
SI La condition C est réalisée ALORS continuer l'exécution à l'adresse N SINON continuer en séquence.
REMARQUE:
Les langages internes ne possèdent pas d'instructions répétitives. Pour répéter un traitement, il faut donc
utiliser une combinaison de ruptures de séquences conditionnelles et inconditionnelles, du type:
ADRESSES | INSTRUCTIONS |
A1 | SI la condition C est réalisée ALORS continuer l'exécution à l'adresse A2 |
A1+1 | Instructions séquencielles pouvant modifier
la condition C (sinon, on ne sortirait jamais de la boucle !) |
etc... | |
A2-1 | CONTINUER L'EXECUTION à l'adresse A1 (rebouclage sur A1) |
A2 | Suite du programme... |
COMMENTAIRES:
- L'instruction contenue par l'adresse A1 est une instruction conditionnelle. Donc, si la condition
C n'est pas réalisée, l'exécution se continue en séquence, c'est à dire en A1+1.
- Les cases de A1+1 à A2-2 contiennent des traitements susceptibles de réaliser la condition C.
- On arrive ensuite à l'adresse A2-1, qui contient une instruction de rupture de séquence
inconditionnelle vers l'adresse A1. L'exécution est donc déroutée (rebouclée) vers cette adresse.
- L'instruction contenue par l'adresse A1 est donc réexécutée. Si les instructions de A1+1 à A2-2
n'ont pas réalisé la condition C, on continue en A1+1 comme la première fois, et on reboucle en A1.
- On ne sortira de la boucle que quand C sera réalisée, auquel cas l'instruction contenue par
l'adresse A1 déroutera l'exécution en A2, c'est à dire après la rupture inconditionnelle, provoquant
la fin du rebouclage.
II.3.3.3.AUTRES CAS DE RUPTURE DE SEQUENCE:
La séquence d'exécution peut également être provoquée par la survenue d'un événement ou la détection
d'une anomalie de fonctionnement. Dans ce cas, c'est l'unité d'entrée-sortie ou l'unité de traitement
qui modifient le compteur ordinal. L'opération permet de transférer l'exécution vers l'adresse de début
d'un programme de traitement de cet événement ou de cette anomalie (gestionnaire d'événement). Un tel
déroutement s'appelle INTERRUPTION PRIORITAIRE. L'adresse chargée dans le compteur ordinal
s'appelle: ADRESSE D'INTERRUPTION.
II.3.4.UN PETIT EXEMPLE DE PROGRAMME:
Soit une machine dont le langage interne supporte les instructions suivantes (les codes d'ordre son représentés
par des noms LMA, LAM, etc...):
CODE D'ORDRE | OPERATION REALISEE |
LMA | Charger le contenu de la mémoire dans l'accumulateur. |
LAM | Charger le contenu de l'accumulateur dans la mémoire. |
LIA | Charger la valeur de l'opérande dans l'accumulateur. |
LAES | Charger la valeur de l'accumulateur dans le registre d'entrée-sortie. |
MMA | Multiplier le contenu de la mémoire avec le contenu de l'accumulateur, résultat dans l'accumulateur. |
ADA | Additionner la valeur de l'operande à l'accumulateur, resultat dans l'accumulateur. |
SMA | Soustraire le contenu de la mémoire au contenu de l'accumulateur, résultat dans l'accumulateur. |
JAN | Si l'accumulateur contient une valeur négative, charger la valeur de l'opérande dans le compteur ordinal. |
GOSUB | Ecrire le contenu du registre d'entrée-sortie sur un peripherique. |
Supposons que nous voulions déterminer le plus petit nombre entier dont le carré est supérieur à 100.
Le programme en mémoire ressemblerait à ce qui suit (les adresses mémoires sont choisies arbitrairement
et le mode d'indexation est représenté par deux lettres: im pour immédiat, di pour direct):
ADRESSE MEMOIRE | CODE D'ORDRE | MODE D'ADESSAGE | VALEUR DE L'OPERANDE | ACTION |
1000 | LIA | im | 100 | 100 --> accumulateur |
1001 | LAM | di | 1013 | Accumulateur --> case 1013 (la case 1013 contient maintenant la valeur 100) |
1002 | LIA | im | 0 | 0 --> accumulateur |
1003 | LAM | di | 1012 | Accumulateur --> case 1012 (la case 1012 va contenir les valeurs successives des entiers) |
1004 | LMA | di | 1012 | Contenu case 1012 --> accumulateur |
1005 | ADA | Im | 1 | Contenu accumulateur + 1 --> accumulateur |
1006 | MMA | di | 1012 | Contenu case 1012 * Contenu accumulateur --> Accumulateur |
1007 | SMA | di | 1013 | Contenu accumulateur – 100 --> accumulateur |
1008 | JAN | Im | 1004 | Si la valeur de l'accumulateur est devenue négative, renvoyer l'exécution à la case 1004 |
1009 | LMA | di | 1012 | Contenu case 1013 --> Accumulateur |
1010 | LAES | Sans objet | Sans objet | Contenu accumulateur --> registre d'entrée-sortie |
1011 | GOSUB | Sans objet | Id. de l'écran | Emettre le contenu du registre d'entrée-sortie vers l'ecran |
La logique de ce fragment de programme peut être décrite par le logigramme suivant:
II.3.5.LES LANGAGES EVOLUES:
II.3.5.1.CARACTERISTIQUES DES LANGAGES EVOLUES:
Ces types de langages se rapprochent beaucoup par leur forme des langages algorithmiques, tout en obeissant
à des rêgles syntaxiques et sémantiques rigoureuses. Ils diffèrent essentiellement des langages internes par
le fait que les opérations et les données sont représentées par des symbôles littéraux. Par exemple:
- Un type d'opération est représenté par un MOT-CLEF ou un OPERATEUR littéral alors que dans un
langage interne, il est représenté directement par son identifiant dans le code d'ordre d'un type de
processeur donné (ce qui le rend dépendant de ce type de processeur).
- Une donnée est représentée par un NOM, encore appelé ETIQUETTE (alors que dans un langage interne,
une donnée est représentée par un NOMBRE, qui est soit sa valeur soit son adresse en mémoire). Dans
un langage évolué, une donnée ressemble donc à une VARIABLE ou une CONSTANTE (aux sens mathématiques
de ces termes), indépendante de toute implantation en mémoire.
EXEMPLES:
En langage C, une addition s'écrit:
C = A + B;
(A, B et C sont des étiquettes de données , = et + sont des opérateurs littéraux.)
En langage C également, une instruction de rupture conditionnelle s'écrira:
if ( Temperature>0) { Etat = ''Liquide'' } else { Etat = ''Glace'' }
(Temperature et Etat sont des données, ''Liquide » et « Glace » sont des constantes littérales,
if, then, else et endif sont des mots-clefs, > est un opérateur de comparaison).
Les codes objets ainsi écrits ont l'avantage de faire abstraction du type de processeur et de l'implantation
en mémoire du programme. Cependant, ils ne sont pas exécutables par un processeur. Pour obtenir un
programme exécutable, il faut (au minimum) attribuer à chaque étiquette une adresse mémoire et remplacer
chaque directive de langage évolué par une séquence d'instructions en langage machine équivalente.
Ce type de traduction est effectué automatiquement par des logiciels appelés soit COMPILATEURS/EDITEURS
DE LIENS, soit INTERPRETEURS:
- Les COMPILATEURS/EDITEURS DE LIENS traduisent les langages évolués dans un langage intermédiaire
proche du langage interne de la machine, mais qui représente toujours les adresses sous forme de
symboles (LANGAGES INTERNES TRANSLATABLES). D'autres logiciels, appelés CHARGEURS remplacent ces
adresses symboliques par des adresses réelles au moment du chargement du programme en mémoire.
Les langages évolués compilés les plus courants sont les langages C et C++, ADA, Fortran, etc.
- Les INTERPRETEURS interprètent chaque directive de langage évolué par le lancement de l'exécution d'un
programme en langage interne équivalent. Il n'y a donc pas d'étape intermédiaire (de ce fait, l'interprétation
d'un code est toujours plus lente que son exécution après compilation). Les langages évolués interprétés les
plus courants sont Java, Javascript, PHP, etc.
- Pour un langage évolué donné, il peut exister à la fois un compilateur et un interpréteur.
II.3.5.2.UTILITE DES LANGAGES EVOLUES:
Un programme directement réalisé en langage interne présente les défauts suivants:
- Il ne peut fonctionner que sur un seul type de processeur, doté du code d'ordre utilisé pour
écrire le programme. Pour le «porter» sur une machine de type différent, il faut donc le
réécrire totalement: il serait évidemment très ennuyeux d'avoir à réécrire Internet Explorer ou Word
pour tous les types de processeurs existants!
- Il ne peut être chargé qu'à partir d'une adresse déterminée de la mémoire. Il est donc difficile de
le faire cohabiter avec d'autres programmes.
- Le code objet est très peu lisible, ce qui rend la mise au point et la reprise par d'autres
développeurs difficiles.
- Il est très difficile à modifier, car trop lié aux environnements matériel et d'utilisation.
A l'inverse, un programme en langage évolué est:
- Indépendant de tout type de processeur (pour l'adapter à un processeur, il suffit de choisir le
compilateur ou l'interpréteur qui convient).
- Indépendant de l'implantation mémoire (il peut être chargé dans n'importe quelle zone de mémoire
libre).
- Très lisible et facilement modifiable.
De ce fait, les développeurs n'utilisent pratiquement jamais directement le langage interne. En général,
ils programment dans des langages dits «évolués».
II.3.5.3.EXEMPLE DE COMPILATION:
Le schéma ci-dessous donne un aperçu du travail effectué par un compilateur (le programme correspond à
l'exemple du paragraphe II.2.4):
II.3.5.4.PHASES DE VIE D'UN PROGRAMME:
Le schéma suivant représente les étapes de la vie d'un programme, depuis sa création en langage
évolué compilable jusqu'à son exécution:
- PHASE 1-Développement: Le développeur crée un programme sous la forme de un ou plusieurs fichiers
de texte en LANGAGE EVOLUE. Ce programme n'est pas directement exécutable par un processeur. Les
fichiers obtenus sont appelés FICHIERS SOURCES du programme.
- PHASE 2-Production: Chaque fichier source est d'abord traduit en LANGAGE INTERNE SYMBOLIQUE (ou
LANGAGE D'ASSEMBLAGE) par des logiciels appelés COMPILATEURS. Un autre programme appelé EDITEUR DE
LIENS se charge de réunir et lier entre eux les différents fichiers résultant de la compilation.
Le résultat est assez proche du langage interne, à la seule différence que les adresses mémoires
ne sont pas définies (on dit qu'elles sont TRANSLATABLES). Ce type de fichier est dit «exécutable».
- PHASE 3-Execution: Au moment de l'exécution, le système d'exploitation détermine en fonction des
disponibilités les zones de mémoire qu'il peut attribuer au programme. Il lance alors un logiciel
appelé CHARGEUR qui remplace les adresses symboliques du fichier par des adresses numériques et
implante le programme obtenu en mémoire. Le système d'exploitation peut alors gérer la vie du
programme en mémoire (lancement, arrêt, mise en attente d'un événement ou d'une ressource,
désallocation de la mémoire en fin d'utilisation).
REMARQUE:
Lorsque le système d'exploitation lance l'exécution d'un programme, il ne charge pas toujours la
totalité de ce programme en mémoire: il peut n'en charger qu'une partie (dite résidente) qui assure
l'enchaînement des traitements. Les parties supportant chaque traitement ne sont chargées que lorsque
la partie résidente en fait la demande au système d'exploitation. La mémoire est désallouée dès que
le programme est terminé. Ce type de traitement, qui permet de minimiser l'occupation mémoire des
programmes, est souvent appliqué aux systèmes d'exploitation eux-mêmes, à cause de leur très fort
volume: seul un NOYAU est chargé en résident permanent, les autres fonctions étant chargées à la demande.
II.3.6.PROCESSUS ET PARALLELISME:
II.3.6.1.NOTION DE PROCESSUS:
Nous avons vu plus haut que pour exécuter un programme, il faut d'abord en charger une copie (on dit une
INSTANCE) en mémoire centrale. Le processeur peut alors exécuter les instructions qu'il contient. Une
INSTANCE en cours d'exécution d'un programme donné constitue un PROCESSUS logiciel (ne pas confondre avec un
PROCESSEUR, qui est le composant matériel qui exécute le PROCESSUS).
REMARQUE:
Le mot TACHE est souvent enployé à la place de PROCESSUS. Bien que ces deux notions ne soient pas
totalement identiques, nous pouvons les confondre dans le cadre du présent cours.
II.3.6.2.NOTION DE THREAD:
Pour un processeur, exécuter un logiciel revient à parcourir une suite d'instructions de ce programme.
Nous avons vu plus haut que l'ordre d'exécution des instruction n'est pas totalement déterminé par
l'ordre d'écriture du programme, mais plutôt par la logique de celui-ci, car il existe des instructions
de rupture conditionnelle de séquence. Cette suite, ce «fil d'instructions» contrôle») est désigné par
le terme anglais THREAD (que l'on peut traduire par fil ou filin).
Les threads sont donc les «unités d'exécution» des logiciels. Pour s'exécuter, un processus doit
comprendre au moins un thread (appelé thread principal). De plus, le code objet d'un processus peut
être conçu de façon à pouvoir être exécuté sous la forme de plusieurs threads. Par exemple, un
navigateur internet peut comprendre un thread qui traite les entrées-sorties sur la connexion internet
et un autre thread qui s'occupe de l'interface avec l'utilisateur (clavier, souris, écran).
II.3.6.3.NOTION DE PARALLELISME:
L'exécution de deux ou plusieurs threads est qualifiée de PARALLELE lorsqu'un observateur a l'IMPRESSION
que ceux-ci s'exécutent SIMULTANÉMENT.
II.3.6.4.EXECUTION EN PARALLELISME APPARENT:
A un instant donné, plusieurs programmes peuvent résider dans la mémoire centrale d'un ordinateur. Un
processeur se conformant au modèle de VON NEUMAN ne peut en exécuter qu'un seul à la fois. Cependant,
il est possible de partager le temps d'exécution du processeur entre plusieurs threads. Nous donnons
ci-dessous deux des méthodes possibles:
UTILISATION DES PERIODES D'ATTENTE PASSIVE DES THREADS (MULTIPROGRAMMATION):
La plupart des programmes sont contraints, à certains stades de leur exécution, d'attendre la libération
d'une ressource ou la survenue d'un événement conditionnant la poursuite de leur exécution.
EXEMPLES:
- Un thread de programme ayant besoin de lire ou écrire sur un disque dur doit attendre que
les têtes de lecture-écriture soient bien positionnées, ce qui peut demander quelques dizaines
de millisecondes d'attente.
- Un thread de dialogue homme-machine peut rester en attente d'une commande de l'utilisateur
ou de la saisie d'une réponse à une question pendant un temps indéterminé.
- Un thread recevant de la vidéo à afficher en « streaming » sur l'écran de l'ordinateur
passe une grande partie du temps à attendre l'arrivée de messages sur la liaison internet.
En fait, la plupart des programmes interactifs (les navigateurs, par exemple), passent beaucoup plus
de temps en attente (d'une commande, d'un message, de la libération d'un équipement, etc) qu'en
exécution. Il est alors possible de mettre à profit ces périodes d'attente pour exécuter d'autres
threads, et un observateur a alors l'impression que ces threads s'exécutent simultanément. Le schéma
suivant illustre cette technique appelée MULTI-PROGRAMMATION:
PROGRAMMATION EN TEMPS PARTAGE:
Cette autre technique consiste en un partage à parts égale du temps d'exécution du processeur
entre les threads prèts à être exécutés: une certaine durée DT est allouée par roulement à
chaque thread prèt, comme l'illustre le schema ci-dessous (comme DT est choisie faible par
rapport à la perception humaine (par exemple, DT=100 ms), l'utilisateur a l'impression d'une exécution
simultanée. Cette technique s'appelle «exécution en TEMPS PARTAGE» (ou TIME SHEARING en anglais):
REMARQUES:
- La gestion de la multiprogrammation et du partage du temps d'exécution est effectuée
par le système d'exploitation de l'ordinateur, que nous aborderons au chapitre IV.
- Les deux techniques (multiprogrammation et temps partagé) sont employées simultanément.
De ce fait, un thread en temps partagé qui tombe en attente avant la fin de sa durée Dt
allouée perd immédiatement le processeur au profit d'un autre thread.
NOTION DE FLOT DE CONTRÔLE:
La suite réelle des instructions exécutées par le processeur est appelée FLOT (OU FLUX) DE CONTRÔLE.
En mode multitâche, comme nous l'avons vu ci-dessus, ce flot de contrôle peut passer d'un thread à
un autre au grès de l'arbitrage fait par le système d'exploitation. Executer un thread revient à
lui attribuer le flot de contrôle d'un processeur (ne pas confondre la notion de flot de contrôle
avec celle de thread, qui concerne la suite d'instruction exécutées par un processeur à l'intérieur
d'un programme donné, en fonction de la logique de ce programme: le flot de contrôle est constitué
de segments de threads, exécutés en fonction des rêgles de partage du temps d'exécution).
II.3.6.5.EXECUTION EN PARALLELISME REEL:
Pour que plusieurs threads puissent réellement s'exécuter en parallèle, il faut pouvoir disposer
d'autant d'exécutants que de threads. De telles machines équipées de plusieurs processeurs indépendants
sont appelées «SYSTEMES MULTIPROCESSEURS».